class_name State
extends RefCounted

## 状态名称
var name: String

## 父状态机
var parent: StateMachine

## 持有该状态的对象
var owner: Variant

## 存储于状态中的数据
## { name-String: value-Variant }
var blackboard: Dictionary

## 状态转换
var _transitions: Dictionary

## 状态组件
var _components: Dictionary


func _init(init_name: String) -> void:
	name = init_name
	parent = null
	owner = null
	blackboard = {}
	_transitions = {}


func get_state(path: String) -> State:
	return self if path.is_empty() else null
	

func get_leaf_state() -> State:
	return self


func get_leaf_state_path() -> String:
	return name


func dispatch_event(event_name: String) -> bool:
	for transition: EventStateTransition in _transitions.values():
		if transition != null and\
		   transition.event_name == event_name and\
		   transition.try_condition():
			change_state(transition.try_transition(), transition.on_transit)
			if transition.handled:
				return true
	return false


func add_component(component: StateComponent) -> void:
	if component == null:
		Logger.error("Component should not be null.")
		return
	
	if _components.has(component.name):
		Logger.error("State already has the same component with given name '%s'." % component.name)
		return

	component.parent = self
	_components[component.name] = component
	component.on_setup()


func remove_component(component_name: String) -> void:
	if not _components.has(component_name):
		Logger.error("No such component with given name '%s'." % component_name)
		return
	
	_components.erase(component_name)


func activate_component(component_name: String) -> void:
	var component = _components.get(component_name)
	if component == null:
		Logger.error("No such component with given name '%s'." % component_name)
		return
	
	component.active = true


func deactivate_component(component_name: String) -> void:
	var component = _components.get(component_name)
	if component == null:
		Logger.error("No such component with given name '%s'." % component_name)
		return
	
	component.active = false


func change_state(target_state_name: String, on_transit: Callable) -> bool:
	if not parent:
		Logger.error("Can not change the state '%s' without parent state machine." % name)
		return false

	if not parent._states.has(target_state_name):
		Logger.error("Target state do not have the same state machine of this state '%s'." % name)
		return false

	if target_state_name == "any":
		Logger.error("Can not change to 'any'.")
		return false

	if target_state_name == parent.current_state_name:
		return false

	parent._states[parent.current_state_name].on_exit()
	parent.current_state_name = target_state_name
	on_transit.call()
	parent._states[target_state_name].on_enter()
	return true


func on_setup() -> void:
	_on_setup()


func on_enter() -> void:
	_on_enter()
	
	for component: StateComponent in _components.values():
		component.on_enter()

	if name == "ghost":
		if not (\
			try_all_transitions() or\
			(parent and parent._states.has("any") and parent._states["any"].try_all_transitions())
		):
			Logger.fatal("State 'ghost' must be exited immediately, its 'on_update' function should not be invoked.")


func try_all_transitions() -> bool:
	for transition: StateTransition in _transitions.values():
		if transition is not EventStateTransition and\
		   transition.try_condition() and\
		   change_state(transition.try_transition(), transition.on_transit):
			return true
	return false


func on_update(delta: float) -> void:
	if not try_all_transitions():
		_on_update(delta)
	
		for component: StateComponent in _components.values():
			component.on_update(delta)


func on_exit() -> void:
	_on_exit()
	
	for component: StateComponent in _components.values():
		component.on_exit()


#region Need to override
func _on_setup() -> void:
	pass


func _on_enter() -> void:
	pass


func _on_update(_delta: float) -> void:
	pass


func _on_exit() -> void:
	pass
#endregion
